import asyncio
import json
import os
import string
import sys
import time
import traceback
import random

import numpy as np
import qasync
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget,
                             QVBoxLayout, QPushButton, QLabel, QSplitter, QHBoxLayout, QScrollArea, QSlider, QGroupBox,
                             QLineEdit, QListWidget, QMessageBox, QSpacerItem, QCheckBox, QDialog)
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QMutex, QElapsedTimer, QMutexLocker, QTimer
from PyQt5.QtGui import QImage, QPixmap, QColor
import pybullet as p
import pybullet_data

from Switch import Switch
from api.apix import ApiX
from encrypt_all_files import encrypt_file
from log.log_util import LogUitl

RANDOM_ACTIONS=[
  {
    "kLeftElbow": 0.542, "kLeftShoulderPitch": 1.873, "kLeftShoulderRoll": 1.015, "kLeftShoulderYaw": -1.309,
    "kLeftWristPitch": 0.887, "kLeftWristRoll": -0.432, "kLeftWristYaw": 0.765, "kRightElbow": 1.896,
    "kRightShoulderPitch": -2.145, "kRightShoulderRoll": 0.873, "kRightShoulderYaw": 1.047, "kRightWristPitch": -1.214,
    "kRightWristRoll": 1.305, "kRightWristYaw": -0.598, "kWaistPitch": 0.256, "kWaistRoll": -0.193, "kWaistYaw": 0.412
  },
  {
    "kLeftElbow": -0.327, "kLeftShoulderPitch": -2.456, "kLeftShoulderRoll": -1.205, "kLeftShoulderYaw": 2.094,
    "kLeftWristPitch": -0.675, "kLeftWristRoll": 1.824, "kLeftWristYaw": -0.912, "kRightElbow": 0.123,
    "kRightShoulderPitch": 1.987, "kRightShoulderRoll": -1.432, "kRightShoulderYaw": -0.523, "kRightWristPitch": 0.348,
    "kRightWristRoll": -1.759, "kRightWristYaw": 1.407, "kWaistPitch": -0.418, "kWaistRoll": 0.307, "kWaistYaw": -0.685
  },
  {
    "kLeftElbow": 1.987, "kLeftShoulderPitch": 0.765, "kLeftShoulderRoll": 0.654, "kLeftShoulderYaw": -0.785,
    "kLeftWristPitch": 1.432, "kLeftWristRoll": -1.205, "kLeftWristYaw": 0.298, "kRightElbow": -0.876,
    "kRightShoulderPitch": 2.309, "kRightShoulderRoll": 0.598, "kRightShoulderYaw": 1.571, "kRightWristPitch": -0.912,
    "kRightWristRoll": 0.874, "kRightWristYaw": -1.024, "kWaistPitch": 0.098, "kWaistRoll": -0.452, "kWaistYaw": 0.873
  },
  {
    "kLeftElbow": 2.012, "kLeftShoulderPitch": -1.234, "kLeftShoulderRoll": 1.876, "kLeftShoulderYaw": 1.919,
    "kLeftWristPitch": -1.025, "kLeftWristRoll": 0.598, "kLeftWristYaw": -0.407, "kRightElbow": 1.432,
    "kRightShoulderPitch": 0.654, "kRightShoulderRoll": -0.765, "kRightShoulderYaw": -2.094, "kRightWristPitch": 0.785,
    "kRightWristRoll": -0.298, "kRightWristYaw": 0.654, "kWaistPitch": -0.215, "kWaistRoll": 0.164, "kWaistYaw": -0.329
  },
  {
    "kLeftElbow": -0.876, "kLeftShoulderPitch": 2.145, "kLeftShoulderRoll": -0.432, "kLeftShoulderYaw": -1.047,
    "kLeftWristPitch": 0.298, "kLeftWristRoll": 1.305, "kLeftWristYaw": 1.024, "kRightElbow": 2.094,
    "kRightShoulderPitch": -0.912, "kRightShoulderRoll": 1.024, "kRightShoulderYaw": 0.407, "kRightWristPitch": -0.654,
    "kRightWristRoll": 1.876, "kRightWristYaw": -0.785, "kWaistPitch": 0.376, "kWaistRoll": -0.098, "kWaistYaw": 0.542
  }
]

CODE_TO_NAME = {
    "kLeftShoulderPitch": "左肩关节俯仰",
    "kLeftShoulderRoll": "左肩关节侧滚",
    "kLeftShoulderYaw": "左肩关节偏航",
    "kLeftElbow": "左肘关节",
    "kLeftWristRoll": "左手腕侧滚",
    "kLeftWristPitch": "左手腕俯仰",
    "kLeftWristYaw": "左手腕偏航",
    "kRightShoulderPitch": "右肩关节俯仰",
    "kRightShoulderRoll": "右肩关节侧滚",
    "kRightShoulderYaw": "右肩关节偏航",
    "kRightElbow": "右肘关节",
    "kRightWristRoll": "右手腕侧滚",
    "kRightWristPitch": "右手腕俯仰",
    "kRightWristYaw": "右手腕偏航",
    "kWaistYaw": "腰部偏航",
    "kWaistRoll": "腰部侧滚",
    "kWaistPitch": "腰部俯仰"
}


JOINT_DATA_WITH_DEFAULT = [
    {"code": "kLeftShoulderPitch", "name": CODE_TO_NAME["kLeftShoulderPitch"], "min": -3.0892, "default": 0.292139, "max": 2.6704, "current": 0.292139},
    {"code": "kLeftShoulderRoll", "name": CODE_TO_NAME["kLeftShoulderRoll"], "min": -1.5882, "default": 0.220713, "max": 2.2515, "current": 0.220713},
    {"code": "kLeftShoulderYaw", "name": CODE_TO_NAME["kLeftShoulderYaw"], "min": -2.618, "default": -0.00117445, "max": 2.618, "current": -0.00117445},
    {"code": "kLeftElbow", "name": CODE_TO_NAME["kLeftElbow"], "min": -1.0472, "default": 0.977721, "max": 2.0944, "current": 0.977721},
    {"code": "kLeftWristRoll", "name": CODE_TO_NAME["kLeftWristRoll"], "min": -1.9722, "default": 0.050933, "max": 1.9722, "current": 0.050933},
    {"code": "kLeftWristPitch", "name": CODE_TO_NAME["kLeftWristPitch"], "min": -1.6144, "default": 0.0229714, "max": 1.6144, "current": 0.0229714},
    {"code": "kLeftWristYaw", "name": CODE_TO_NAME["kLeftWristYaw"], "min": -1.6144, "default": -0.0037966, "max": 1.6144, "current": -0.0037966},
    {"code": "kRightShoulderPitch", "name": CODE_TO_NAME["kRightShoulderPitch"], "min": -3.0892, "default": 0.291504, "max": 2.6704, "current": 0.291504},
    {"code": "kRightShoulderRoll", "name": CODE_TO_NAME["kRightShoulderRoll"], "min": -2.2515, "default": -0.226418, "max": 1.5882, "current": -0.226418},
    {"code": "kRightShoulderYaw", "name": CODE_TO_NAME["kRightShoulderYaw"], "min": -2.618, "default": 0.00385892, "max": 2.618, "current": 0.00385892},
    {"code": "kRightElbow", "name": CODE_TO_NAME["kRightElbow"], "min": -1.0472, "default": 0.977841, "max": 2.0944, "current": 0.977841},
    {"code": "kRightWristRoll", "name": CODE_TO_NAME["kRightWristRoll"], "min": -1.9722, "default": -0.101027, "max": 1.9722, "current": -0.101027},
    {"code": "kRightWristPitch", "name": CODE_TO_NAME["kRightWristPitch"], "min": -1.6144, "default": -0.060039, "max": 1.6144, "current": -0.060039},
    {"code": "kRightWristYaw", "name": CODE_TO_NAME["kRightWristYaw"], "min": -1.6144, "default": 0.0171883, "max": 1.6144, "current": 0.0171883},
    {"code": "kWaistYaw", "name": CODE_TO_NAME["kWaistYaw"], "min": -2.618, "default": -0.00121866, "max": 2.618, "current": -0.00121866},
    {"code": "kWaistRoll", "name": CODE_TO_NAME["kWaistRoll"], "min": -0.52, "default": 0.0, "max": 0.52, "current": 0.0},
    {"code": "kWaistPitch", "name": CODE_TO_NAME["kWaistPitch"], "min": -0.52, "default": 0.0, "max": 0.52, "current": 0.0}
]

class PhysicsThread(QThread):
    update_signal = pyqtSignal()
    render_signal = pyqtSignal()
    joint_signal = pyqtSignal(list)

    def __init__(self):
        super().__init__()
        self.running = True
        self.mutex = QMutex()
        self.physicsClient = p.connect(p.DIRECT)
        p.setAdditionalSearchPath(pybullet_data.getDataPath())
        p.setGravity(0, 0, 0)

        # 性能计数器
        self.frame_counter = 0
        self.last_fps_time = QElapsedTimer()
        self.last_fps_time.start()

    def transform_string(self, s):
        parts = s.split('_')
        parts_without_joint = parts[:-1]
        capitalized_parts = [part.capitalize() for part in parts_without_joint]
        return 'k' + ''.join(capitalized_parts)

    def run(self):
        try:
            # 初始化物理世界
            # self.planeId = p.loadURDF("plane.urdf")
            # 加载机器人URDF（会自动加载关联的STL和配置文件）
            robotStartPos = [0, 0, 1]
            robotStartOrientation = p.getQuaternionFromEuler([0, 0, -1])
            self.robotId = p.loadURDF("./g1_description/g1_29dof_with_hand_rev_1_0.urdf", robotStartPos, robotStartOrientation,useFixedBase=True)
            p.createConstraint(self.robotId, -1, -1, -1, p.JOINT_FIXED, [0, 0, 0], [0, 0, 0], [0, 0, 1])
            p.setPhysicsEngineParameter(fixedTimeStep=1 / 240)  # 物理步长

            # 获取关节信息
            movable_joints = []
            numJoints = p.getNumJoints(self.robotId)
            print(f"机器人共有 {numJoints} 个关节")
            for i in range(numJoints):
                jointInfo = p.getJointInfo(self.robotId, i)
                joint_name = self.transform_string(jointInfo[1].decode('utf-8'))
                min_angle = jointInfo[8]
                max_angle = jointInfo[9]
                movable_joints.append(dict(joint_index = i, joint_name=joint_name, min_angle=min_angle, max_angle=max_angle))
                # print(f"关节 {i}: {jointInfo[1].decode('utf-8')}, {min_angle},{max_angle} ")

            self.joint_signal.emit(movable_joints)

            # 主循环
            while self.running:
                with QMutexLocker(self.mutex):
                    p.stepSimulation()

                # 控制物理更新频率
                self.msleep(4)  # 约240Hz物理模拟

                # 降低渲染信号频率（约30Hz）
                if self.frame_counter % 8 == 0:
                    self.render_signal.emit()

                self.frame_counter += 1

        except Exception as e:
            print(f"物理线程错误: {str(e)}")
        finally:
            p.disconnect()

    def stop(self):
        self.running = False
        self.wait()


class MainWindow(QMainWindow):

    min_gesture_duration = 0.2


    def __init__(self, logger, loop):
        super().__init__()
        self.cam_width = 420
        self.cam_height = 760
        self.last_render_time = QElapsedTimer()
        self.joint_sliders = {}  # 存储滑块控件的字典

        self.loop = loop
        self.logger = logger
        self.apix = ApiX(self.logger)

        self.available_joints = []
        self.action_counter = 0
        self.actions = dict()
        self.current_modify_action_idx = None

        self.scroll_content_layout = None
        self.all_sliders = []

        self.default_cam_yaw = 30  # 初始水平旋转角度
        self.default_cam_pitch = -15  # 初始俯仰角度
        self.default_cam_distance = 1.4  # 观察距离

        # 添加相机控制相关属性
        self.mouse_dragging = False
        self.last_mouse_pos = None
        self.cam_yaw = self.default_cam_yaw  # 初始水平旋转角度
        self.cam_pitch = self.default_cam_pitch  # 初始俯仰角度
        self.cam_distance = self.default_cam_distance  # 观察距离
        self.cam_target = [0, 0, 0.9]  # 观察目标点

        # 添加动画相关属性
        self.animation_timer = QTimer(self)
        self.animation_timer.timeout.connect(self.smooth_reset)
        self.animation_start_time = 0
        self.reset_duration = 2.0  # 过渡时间（秒）

        # 持续同步机器人数据
        self.sync_robot_timer = QTimer(self)
        self.sync_robot_timer.timeout.connect(self.sync_robot_action_data_continuous)
        self.sync_robot_interval = 100
        self.sync_action_continuous_btn:QPushButton = None

        #是否将数据同步下发到实体机器人
        self.is_sync_to_robot = True

        # 新增用于显示当前动作的标签
        self.current_action_label = None

        # 新增动画控制相关属性
        self.current_action_index = 0
        self.is_paused = False
        self.active_timers = []
        self.sorted_action_keys = []


        self.initUI()
        self.initCamera()
        self.initPhysics()



    def initUI(self):
        self.setWindowTitle("机器人编舞")
        self.setGeometry(100, 100, 1200, 800)

        # 主布局
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        main_layout = QHBoxLayout(main_widget)
        main_layout.setContentsMargins(0, 0, 0, 0)

        # 左侧控制面板
        control_scroll = QScrollArea()
        control_scroll.setMinimumWidth(350)
        control_scroll.setWidgetResizable(True)
        control_widget = QWidget()
        self.control_layout = QVBoxLayout(control_widget)
        control_scroll.setWidget(control_widget)

        # 中间渲染视图
        self.render_widget = QWidget()
        render_layout = QVBoxLayout(self.render_widget)
        render_layout.setContentsMargins(0, 0, 0, 0)

        render_btns_panel = QWidget()
        render_btns_layout = QHBoxLayout(render_btns_panel)

        set_host_btn = QPushButton("设置IP")
        set_host_btn.setFixedWidth(75)
        set_host_btn.setFixedHeight(30)
        set_host_btn.setStyleSheet("""
                                            QPushButton {{
                                                background-color: {};
                                                color: white;
                                                border: none;
                                                font-size: 12px;
                                                padding: 8px;
                                                border-radius: 6px;
                                            }}
                                            QPushButton:pressed {{
                                                background-color: {};
                                            }}
                                        """.format(
            "#71797D",
            QColor("#71797D").darker(160).name()
        ))
        set_host_btn.clicked.connect(self.set_api_ip_address)
        render_btns_layout.addWidget(set_host_btn)


        sync_action_btn = QPushButton("同步动作")
        sync_action_btn.setFixedWidth(75)
        sync_action_btn.setFixedHeight(30)
        sync_action_btn.setStyleSheet("""
                                    QPushButton {{
                                        background-color: {};
                                        color: white;
                                        border: none;
                                        font-size: 12px;
                                        padding: 8px;
                                        border-radius: 6px;
                                    }}
                                    QPushButton:pressed {{
                                        background-color: {};
                                    }}
                                """.format(
            "#71797D",
            QColor("#71797D").darker(160).name()
        ))
        sync_action_btn.clicked.connect(self.start_sync_robot_action_data)
        render_btns_layout.addWidget(sync_action_btn)

        switch = Switch()
        switch.toggled.connect(lambda state: self.on_sync_to_robot_change(state))
        render_btns_layout.addWidget(switch)

        self.sync_action_continuous_btn = QPushButton("持续同步动作")
        self.sync_action_continuous_btn.setFixedHeight(30)
        self.sync_action_continuous_btn.setFixedWidth(100)
        self.sync_action_continuous_btn.setStyleSheet("""
                                            QPushButton {{
                                                background-color: {};
                                                color: white;
                                                border: none;
                                                font-size: 12px;
                                                padding: 8px;
                                                border-radius: 6px;
                                            }}
                                            QPushButton:pressed {{
                                                background-color: {};
                                            }}
                                        """.format(
            "#71797D",
            QColor("#71797D").darker(160).name()
        ))
        self.sync_action_continuous_btn.clicked.connect(self.ctl_continuous_sync)
        render_btns_layout.addWidget(self.sync_action_continuous_btn)

        render_layout.addWidget(render_btns_panel)

        self.render_label = QLabel()
        self.render_label.setAlignment(Qt.AlignCenter)
        self.render_label.setMinimumSize(self.cam_width, self.cam_height)
        render_layout.addWidget(self.render_label)

        # 右侧动作面板
        right_panel = QWidget()
        right_panel.setMinimumWidth(350)
        right_layout = QVBoxLayout(right_panel)

        # 上半部分：动作列表
        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setMinimumHeight(520)
        scroll_area.setStyleSheet("background-color: #444444; color: white; padding: 5px;border-radius:6px;")
        scroll_content_widget = QWidget()
        self.scroll_content_layout = QVBoxLayout()
        scroll_content_widget.setLayout(self.scroll_content_layout)
        # 将滚动区域内的部件设置到滚动区域中
        scroll_area.setWidget(scroll_content_widget)

        action_header = QWidget()
        action_header_layout = QHBoxLayout(action_header)
        action_header_layout.addWidget(QLabel("动作序列"))

        # 添加当前动作显示标签
        self.current_action_label = QLabel("当前动作：无")
        self.current_action_label.setStyleSheet("color: #75A1B7; font-weight: bold;")
        action_header_layout.addWidget(self.current_action_label)

        right_layout.addWidget(action_header)
        right_layout.addWidget(scroll_area)

        # 中间部分
        middle_panel = QWidget()
        middle_panel.setMinimumHeight(90)
        # middle_panel.setStyleSheet("background-color: #444444; padding:0px; color: white;border-radius:6px;")
        middle_layout = QVBoxLayout(middle_panel)

        middle_top_layout = QHBoxLayout()
        back_default_action_btn = QPushButton("起始姿态")
        back_default_action_btn.setFixedHeight(35)
        btn_color = "#71797D"
        back_default_action_btn.setStyleSheet("""
                            QPushButton {{
                                background-color: {};
                                color: white;
                                border: none;
                                font-size: 12px;
                                border-radius: 6px;
                            }}
                            QPushButton:pressed {{
                                background-color: {};
                            }}
                        """.format(
            btn_color,
            QColor(btn_color).darker(160).name()
        ))
        back_default_action_btn.clicked.connect(lambda _ : self.back_to_default_pose(self.available_joints, delay = 0 , sync = True))
        middle_top_layout.addWidget(back_default_action_btn)

        show_all_action_btn = QPushButton("演示整套动作")
        show_all_action_btn.setFixedHeight(35)
        btn_color = "#75A1B7"
        show_all_action_btn.setStyleSheet("""
                    QPushButton {{
                        background-color: {};
                        color: white;
                        border: none;
                        font-size: 12px;
                        border-radius: 6px;
                    }}
                    QPushButton:pressed {{
                        background-color: {};
                    }}
                """.format(
            btn_color,
            QColor(btn_color).darker(160).name()
        ))
        show_all_action_btn.clicked.connect(self.show_all_actions)
        middle_top_layout.addWidget(show_all_action_btn)
        middle_layout.addLayout(middle_top_layout)

        middle_bottom_layout = QHBoxLayout()
        pause_show_action_btn = QPushButton("暂停演示")
        pause_show_action_btn.setFixedHeight(35)
        btn_color = "#FFAA4F"
        pause_show_action_btn.setStyleSheet("""
                                    QPushButton {{
                                        background-color: {};
                                        color: white;
                                        border: none;
                                        font-size: 12px;
                                        border-radius: 6px;
                                    }}
                                    QPushButton:pressed {{
                                        background-color: {};
                                    }}
                                """.format(
            btn_color,
            QColor(btn_color).darker(160).name()
        ))
        pause_show_action_btn.clicked.connect(lambda _: self.pause_show_action())
        middle_bottom_layout.addWidget(pause_show_action_btn)

        stop_show_all_action_btn = QPushButton("停止演示")
        stop_show_all_action_btn.setFixedHeight(35)
        btn_color = "#FF562E"
        stop_show_all_action_btn.setStyleSheet("""
                            QPushButton {{
                                background-color: {};
                                color: white;
                                border: none;
                                font-size: 12px;
                                border-radius: 6px;
                            }}
                            QPushButton:pressed {{
                                background-color: {};
                            }}
                        """.format(
            btn_color,
            QColor(btn_color).darker(160).name()
        ))
        stop_show_all_action_btn.clicked.connect(self.stop_show_all_action)
        middle_bottom_layout.addWidget(stop_show_all_action_btn)
        middle_layout.addLayout(middle_bottom_layout)

        right_layout.addWidget(middle_panel)

        # 下半部分：输入控制
        bottom_panel = QWidget()
        bottom_layout = QHBoxLayout(bottom_panel)

        # 左侧输入组
        left_input = QWidget()
        left_input_layout = QVBoxLayout(left_input)
        self.duration_input = QLineEdit("1.0")
        left_input_layout.addWidget(QLabel("动作时长(s)"))
        left_input_layout.addWidget(self.duration_input)
        self.save_btn = QPushButton("保存动作")
        left_input_layout.addWidget(self.save_btn)

        # 右侧输入组
        right_input = QWidget()
        right_input_layout = QVBoxLayout(right_input)
        self.name_input = QLineEdit()
        right_input_layout.addWidget(QLabel("动作名称"))
        right_input_layout.addWidget(self.name_input)
        self.export_btn = QPushButton("导出配置")
        right_input_layout.addWidget(self.export_btn)

        bottom_layout.addWidget(left_input)
        bottom_layout.addWidget(right_input)
        right_layout.addWidget(bottom_panel)

        # 三级分割布局
        main_splitter = QSplitter(Qt.Horizontal)
        main_splitter.addWidget(control_scroll)
        main_splitter.addWidget(self.render_widget)
        main_splitter.addWidget(right_panel)

        # 设置伸缩比例：4:5:3
        main_splitter.setStretchFactor(0, 4)
        main_splitter.setStretchFactor(1, 5)
        main_splitter.setStretchFactor(2, 3)

        main_splitter.setHandleWidth(5)
        main_layout.addWidget(main_splitter)

        # 连接信号
        self.save_btn.clicked.connect(self.save_action)
        self.export_btn.clicked.connect(self.export_action)

    def on_sync_to_robot_change(self , state):
        """ 切换是否同步到实体机器人"""
        self.is_sync_to_robot = state

    def save_action(self):
        """保存动作的处理函数"""
        duration = float(self.duration_input.text())
        if duration == "" or duration == 0:
            QMessageBox.warning(self, "提示", f"动作时长不能小于{MainWindow.min_gesture_duration}")
            return

        if self.current_modify_action_idx is not None:
            current_duration = float(self.duration_input.text())
            need_send_values = [round(item["current"], 5) for item in self.available_joints]
            data = dict(type="MOVE", positions=need_send_values, duration=current_duration)
            print(f"----update action: {self.current_modify_action_idx}, {data}")
            self.actions[self.current_modify_action_idx] = data

            target_widget = None
            print(f"-----count(): {self.scroll_content_layout.count()}")
            for idx in range(self.scroll_content_layout.count()):
                item = self.scroll_content_layout.itemAt(idx).widget()
                if item:
                    if item.objectName() and int(item.objectName()) == int(self.current_modify_action_idx):
                        target_widget = item

            if target_widget:
                duration_layout = target_widget.layout()
                duration_label = duration_layout.itemAt(1).widget()
                if duration_label:
                    duration_label.setText(str(current_duration))

            self.current_modify_action_idx = None
            QMessageBox.warning(self, "提示", f"更新成功！")

        else:
            action_name = f"动作{self.action_counter}"
            action_label = QLabel(action_name)

            # 获取当前动作时长
            current_duration = float(self.duration_input.text())
            duration_label = QLabel(str(current_duration))
            duration_label.setFixedWidth(50)  # 设置时长标签宽度

            # 创建查看按钮
            show_button = QPushButton('演示')
            show_button.setObjectName(f"{self.action_counter}")
            show_button.clicked.connect(
                lambda _, layout_index=int(show_button.objectName()): self.show_action(layout_index))

            # 创建修改按钮
            modify_button = QPushButton('修改')
            modify_button.setObjectName(f"{self.action_counter}")
            modify_button.clicked.connect(
                lambda _, layout_index=int(modify_button.objectName()): self.modify_action(layout_index))

            # 创建删除按钮
            delete_button = QPushButton('删除')
            delete_button.setObjectName(f"{self.action_counter}")
            delete_button.clicked.connect(
                lambda _, layout_index=int(delete_button.objectName()): self.delete_action(layout_index))


            h_layout = QHBoxLayout()
            h_layout.setSpacing(10)  # 设置布局内控件间距
            h_layout.addWidget(action_label)
            h_layout.addWidget(duration_label)
            h_layout.addWidget(show_button)
            h_layout.addWidget(modify_button)
            h_layout.addWidget(delete_button)

            # 创建一个容器部件，并设置背景色、固定高度和文字颜色
            container_widget = QWidget()
            # 设置固定高度为 40 像素，你可以根据需要调整
            container_widget.setFixedHeight(40)
            # 设置背景色为灰色，文字颜色为白色，确保对比度
            container_widget.setStyleSheet("background-color: #666666; color: white; padding: 2px;border-radius:6px")
            container_widget.setLayout(h_layout)
            container_widget.setObjectName(f"{self.action_counter}")
            # 移除之前添加的用于避免占满父容器高度的 stretch
            if self.scroll_content_layout.count() > 1 and isinstance( self.scroll_content_layout.itemAt(self.scroll_content_layout.count() - 1), QSpacerItem):
                self.scroll_content_layout.removeItem( self.scroll_content_layout.itemAt(self.scroll_content_layout.count() - 1))

            self.scroll_content_layout.insertWidget(self.action_counter, container_widget)  # 在顶部插入新部件

            # 确保布局底部有一个 stretch 来避免设备布局占满父容器高度
            if self.scroll_content_layout.count() == 1 or not isinstance( self.scroll_content_layout.itemAt(self.scroll_content_layout.count() - 1), QSpacerItem):
                self.scroll_content_layout.addStretch()


            # 缓存当前关节数据 "type": "MOVE",
            need_send_values = [round(item["current"], 5) for item in self.available_joints]
            data = dict(type="MOVE", positions=need_send_values, duration=current_duration)
            print(f"----add action: {data}")
            self.actions[self.action_counter] = data

            self.action_counter += 1  # 计数器加 1

    def pause_show_action(self):
        """ 暂停演示 """
        if self.active_timers:
            self._clear_timers()
            self.is_paused = True
            text = self.current_action_label.text()
            self.current_action_label.setText(f"已暂停（{text}）")

    def stop_show_all_action(self):
        """ 停止演示 """
        self._clear_timers()
        self.is_paused = False
        self.current_action_index = 0
        self.current_action_label.setText("演示已停止")
        # 重置到默认姿态
        self.back_to_default_pose(self.available_joints, delay=0, sync=True)

    def _clear_timers(self):
        """ 清除所有定时器 """
        for timer in self.active_timers:
            timer.stop()
        self.active_timers.clear()

    def show_all_actions(self):
        """ 演示所有动作的处理函数 """

        if not self.actions:
            self.current_action_label.setText("没有可演示的动作！")
            return

        if self.is_paused:
            # 从暂停的位置继续
            start_index = self.current_action_index
            self.is_paused = False
        else:
            # 重新开始
            start_index = 0
            self.current_action_index = 0
            self.sorted_action_keys = sorted(self.actions.keys())

            # 清空之前的定时器
        self._clear_timers()

        # 添加开始提示
        self.current_action_label.setText("即将开始整套动作演示...")

        # 这里可以实现演示所有动作的逻辑
        delay = 0
        total_actions = len(self.sorted_action_keys)

        # 调度每个动作的执行
        for i in range(start_index, total_actions):
            action_key = self.sorted_action_keys[i]
            # 创建动作执行定时器
            action_timer = QTimer()
            action_timer.setSingleShot(True)
            action_timer.timeout.connect(lambda idx=action_key: self._execute_single_action(idx))
            action_timer.start(delay)
            self.active_timers.append(action_timer)

            # 更新延迟时间
            delay += 2000

            # 最后一个动作添加完成提示
            if i == total_actions - 1:
                completion_timer = QTimer()
                completion_timer.setSingleShot(True)
                completion_timer.timeout.connect(self._on_all_actions_complete)
                completion_timer.start(delay)
                self.active_timers.append(completion_timer)

    def _execute_single_action(self, action_key):
        """ 执行单个动作 """
        self.current_action_index += 1
        print(f"-------------current_action_index:{self.current_action_index}")
        self.show_action(action_key)

    def _on_all_actions_complete(self):
        """ 所有动作完成处理 """
        self.current_action_label.setText("整套动作演示完成！")
        self.current_action_index = 0
        self._clear_timers()


    def start_sync_robot_action_data(self):
        asyncio.run_coroutine_threadsafe(self.sync_robot_action_data() , self.loop)

    def set_api_ip_address(self):
        """ 设置API IP 地址 """
        # 创建对话框
        dialog = QDialog(self)
        dialog.setWindowTitle("设置IP地址")
        dialog.setFixedSize(300, 120)

        # 设置布局
        layout = QVBoxLayout(dialog)

        # 添加输入框
        ip_input = QLineEdit(dialog)
        ip_input.setPlaceholderText("请输入IP地址")
        # 可以设置默认IP，例如：ip_input.setText("127.0.0.1")
        layout.addWidget(ip_input)

        # 添加按钮区域
        btn_layout = QHBoxLayout()

        # 确定按钮
        ok_btn = QPushButton("确定", dialog)
        ok_btn.setFixedWidth(75)
        ok_btn.setFixedHeight(30)

        # 取消按钮
        cancel_btn = QPushButton("取消", dialog)
        cancel_btn.setFixedWidth(75)
        cancel_btn.setFixedHeight(30)

        btn_layout.addWidget(ok_btn)
        btn_layout.addWidget(cancel_btn)
        layout.addLayout(btn_layout)

        # 按钮点击事件处理
        def on_ok_clicked():
            # 获取输入的IP地址
            ip_address = ip_input.text().strip()
            if ip_address:
                self.apix.setHost(ip_address)
                print(f"设置的IP地址: {ip_address}")
                # 可以在这里添加IP地址的验证逻辑
                dialog.accept()
            else:
                QMessageBox.warning(dialog, "警告", "请输入IP地址")

        ok_btn.clicked.connect(on_ok_clicked)
        cancel_btn.clicked.connect(dialog.reject)

        # 显示对话框
        dialog.exec_()

    async def sync_robot_action_data(self, add_new_action = False):
        """ 同步当前机器人动作关节数据 """
        current_joint_data = await self.apix.sync_joints_data()
        # idx = random.randint(0, 4)
        # current_joint_data = RANDOM_ACTIONS[idx]
        print(f"---current_joint_data:{current_joint_data}")
        positions = [current_joint_data[item["code"]] for item in JOINT_DATA_WITH_DEFAULT]
        print(f"---positions:{positions}")

        for slider in self.all_sliders:
            slider.blockSignals(True)
        for index, data in enumerate(positions):
            joint_info = self.available_joints[index]
            joint_info["current"] = data
            if index < len(self.all_sliders):
                slider_value = int((joint_info['current'] - joint_info['min_angle']) / ( joint_info['max_angle'] - joint_info['min_angle']) * 100)
                self.all_sliders[index].setValue(slider_value)

                # 将滑块值转换为实际角度
                angle = np.interp(slider_value, [0, 100], [self.all_sliders[index].min_angle, self.all_sliders[index].max_angle])
                self.all_sliders[index].value_label.setText(f"{angle:.8f}")
                for joint_data in self.available_joints:
                    if joint_data["joint_name"] == self.all_sliders[index].joint_name:
                        joint_data["current"] = angle
                self.adjust_joint_position(self.all_sliders[index].joint_index, angle)

        for slider in self.all_sliders:
            slider.blockSignals(False)

        if add_new_action:
            self.save_action()


    def ctl_continuous_sync(self):
        """ 启动一个QTimer """
        if self.sync_action_continuous_btn is None:
            return

        flag = self.sync_action_continuous_btn.objectName()
        if self.sync_robot_timer.isActive():
            self.sync_robot_timer.stop()

        if flag == "1":
            self.sync_action_continuous_btn.setObjectName("0")
            self.sync_action_continuous_btn.setText("持续同步动作")
        else:
            self.sync_robot_timer.start(self.sync_robot_interval)
            self.sync_action_continuous_btn.setObjectName("1")
            self.sync_action_continuous_btn.setText("停止同步动作")

    def sync_robot_action_data_continuous(self):
        """ 持续同步数据 """
        asyncio.run_coroutine_threadsafe(self.sync_robot_action_data(True), self.loop)

    def show_action(self, layout_index):
        """ 演示动作 """
        if layout_index in self.actions:
            action = self.actions[layout_index]
            positions = action['positions']
            action_name = f"动作{layout_index}"
            # 更新当前动作显示
            self.current_action_label.setText(f"当前动作：{action_name}")


            for slider in self.all_sliders:
                slider.blockSignals(True)
            for index, data in enumerate(positions):
                joint_info = self.available_joints[index]
                joint_info["current"] = data
                if index < len(self.all_sliders):
                    slider_value = int((joint_info['current'] - joint_info['min_angle']) / (joint_info['max_angle'] - joint_info['min_angle']) * 100)
                    self.all_sliders[index].setValue(slider_value)

                    # 将滑块值转换为实际角度
                    angle = np.interp(slider_value, [0, 100], [self.all_sliders[index].min_angle, self.all_sliders[index].max_angle])
                    self.all_sliders[index].value_label.setText(f"{angle:.8f}")
                    for joint_data in self.available_joints:
                        if joint_data["joint_name"] == self.all_sliders[index].joint_name:
                            joint_data["current"] = angle

                    self.adjust_joint_position(self.all_sliders[index].joint_index, angle)

            # 执行动作到机器人
            self.play_to_robot()
            for slider in self.all_sliders:
                slider.blockSignals(False)

            # 添加延时清除显示
            # QTimer.singleShot(int(action['duration'] * 1000), lambda: self.clear_current_action(action_name))

    def clear_current_action(self, action_name):
        """ 清除已完成的动作显示 """
        if self.current_action_label.text() == f"当前动作：{action_name}":
            self.current_action_label.setText("当前动作：无")

    def modify_action(self, layout_index):
        """ 修改动作的处理函数 """
        if layout_index in self.actions:
            action = self.actions[layout_index]
            self.current_modify_action_idx = layout_index

            positions = action['positions']
            for index, data in enumerate(positions):
                joint_info = self.available_joints[index]
                joint_info["current"] = data

                if index < len(self.all_sliders):
                    self.all_sliders[index].setValue(int((joint_info['current'] - joint_info['min_angle']) / (joint_info['max_angle'] - joint_info['min_angle']) * 100))


    def delete_action(self, layout_index):
        """ 删除动作的处理函数 """
        print(f"-----layout_index: {layout_index}")
        # 弹出确认提示框
        reply = QMessageBox.question(self, '确认删除', '确定要删除这个动作吗？', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        if reply == QMessageBox.Yes:
            target_widget = None
            print(f"-----count(): {self.scroll_content_layout.count()}")
            for idx in range(self.scroll_content_layout.count()):
                item = self.scroll_content_layout.itemAt(idx).widget()
                if item:
                    if item.objectName() and int(item.objectName()) == layout_index:
                        target_widget = item
            # 从界面上移除对应的控件
            if target_widget:
                target_widget.deleteLater()
                self.scroll_content_layout.removeWidget(target_widget)

            # 从缓存中删除相应的数据项
            if layout_index in self.actions:
                del self.actions[layout_index]
            print(f"-----self.actions: {self.actions}")

            # 当没有动作的时候，删除一个占位符
            if len(self.actions) == 0:
                if self.scroll_content_layout.count() > 1 and isinstance( self.scroll_content_layout.itemAt(self.scroll_content_layout.count() - 1), QSpacerItem):
                    self.scroll_content_layout.removeItem( self.scroll_content_layout.itemAt(self.scroll_content_layout.count() - 1))

    def export_action(self):
        """ 导出动作的处理函数 """
        # 例如，可以将当前关节数据保存到文件
        file_name = "joint_data.json"
        encrypt_file_name = "joint_data.bin"
        file_path = os.path.join(os.path.dirname(__file__), file_name)
        encrypt_file_path = os.path.join(os.path.dirname(__file__), encrypt_file_name)
        actions = list(self.actions.values())

        sequence_name = self.name_input.text()
        if sequence_name != "":
            export_data = dict()
            export_sequence = dict()
            export_sequence[sequence_name] = actions
            export_data["sequences"] = export_sequence
            print(f"----export_data:{export_data}")

            with open('joint_data.json', 'w') as f:
                json.dump(export_data, f)
            self.encrypt_data(file_path, encrypt_file_path)

            # 上传文件

            # 新增动作

        else:
            QMessageBox.warning(self, "错误", f"名称不能为空")

    def encrypt_data(self, src_path, target_path):
        """ 加密数据的处理函数 """
        try:
            success = encrypt_file(src_path, target_path)
            if success:
                QMessageBox.warning(self, "提示", f"导出成功，数据存储在:{target_path}")
        except Exception as e:
            QMessageBox.warning(self, "提示", f"导出失败，数据存储在:{e}")
            pass

    def initCamera(self):

        self.update_view_matrix()
        # 投影矩阵保持不变
        self.proj_matrix = p.computeProjectionMatrixFOV(
            fov=60,
            aspect=self.cam_width / self.cam_height,
            nearVal=0.1,
            farVal=100
        )


    # 添加视图矩阵更新方法
    def update_view_matrix(self):
        self.view_matrix = p.computeViewMatrixFromYawPitchRoll(
            cameraTargetPosition=self.cam_target,
            distance=self.cam_distance,
            yaw=self.cam_yaw,
            pitch=self.cam_pitch,
            roll=0,
            upAxisIndex=2
        )

    # 在MainWindow类中添加以下方法
    def wheelEvent(self, event):
        """处理滚轮事件实现视角缩放"""
        # scroll_amount = event.angleDelta().y() / 120  # 获取垂直滚动量（标准化为±1）
        # # 设置缩放系数（灵敏度）
        # zoom_speed = 0.2
        # # 计算新的观察距离
        # self.cam_distance -= scroll_amount * zoom_speed
        # # 设置距离限制（0.5米到5米之间）
        # self.cam_distance = max(0.5, min(5.0, self.cam_distance))
        # # 更新视图矩阵
        # self.update_view_matrix()
        # # 立即刷新渲染
        # self.safe_render()
        # event.accept()
        pos_in_render = self.render_widget.mapFromParent(event.pos())
        if self.render_widget.rect().contains(pos_in_render):
            scroll_amount = event.angleDelta().y() / 120
            zoom_speed = 0.2
            self.cam_distance -= scroll_amount * zoom_speed
            self.cam_distance = max(0.5, min(5.0, self.cam_distance))
            self.update_view_matrix()
            self.safe_render()
            event.accept()
        else:
            event.ignore()


    # 添加鼠标事件处理函数
    def mousePressEvent(self, event):
        # if event.button() == Qt.LeftButton:
        #     self.mouse_dragging = True
        #     self.last_mouse_pos = event.pos()
        #     self.setCursor(Qt.ClosedHandCursor)
        # 转换事件位置到渲染部件的坐标系
        pos_in_render = self.render_widget.mapFromParent(event.pos())
        if self.render_widget.rect().contains(pos_in_render):
            if event.button() == Qt.LeftButton:
                self.mouse_dragging = True
                self.last_mouse_pos = event.pos()
                self.setCursor(Qt.ClosedHandCursor)
                event.accept()
            else:
                event.ignore()
        else:
            super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if self.mouse_dragging and self.last_mouse_pos:
            # 计算鼠标移动增量
            delta = event.pos() - self.last_mouse_pos
            delta_x = delta.x()
            delta_y = delta.y()

            # 调整视角角度
            self.cam_yaw -= delta_x * 0.5  # 水平移动灵敏度
            self.cam_pitch -= delta_y * 0.5  # 垂直移动灵敏度

            # 限制水平旋转角度范围（-30°到90°）
            self.cam_yaw = max(-30, min(90, self.cam_yaw))
            # 限制俯仰角度范围（-60°到60°）
            self.cam_pitch = max(-60, min(60, self.cam_pitch))

            # 更新视图矩阵
            self.update_view_matrix()

            # 请求立即渲染更新
            self.safe_render()

            # 更新最后鼠标位置
            self.last_mouse_pos = event.pos()
            event.accept()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton and self.mouse_dragging:
            if event.button() == Qt.LeftButton:
                self.mouse_dragging = False
                self.last_mouse_pos = None
                self.setCursor(Qt.ArrowCursor)
            event.accept()

    def initPhysics(self):
        self.physics_thread = PhysicsThread()
        self.physics_thread.render_signal.connect(self.safe_render)
        self.physics_thread.joint_signal.connect(self.on_joint_info)
        self.physics_thread.start()
        self.last_render_time.start()

    def safe_render(self):
        """节流渲染更新"""
        if self.last_render_time.hasExpired(33):  # 30Hz更新
            self.updateRender()
            self.last_render_time.restart()

    def create_slider_control(self, joint_info):
        """创建单个关节的滑块控件（包含+-按钮）"""
        joint_name = CODE_TO_NAME.get(joint_info['joint_name'], joint_info['joint_name'])
        container = QGroupBox(joint_name)
        layout = QVBoxLayout(container)

        # 数值显示
        value_label = QLabel(f"{joint_info['default']:.8f}")
        layout.addWidget(value_label)

        # 滑块和按钮的水平布局
        slider_layout = QHBoxLayout()

        # 减号按钮
        btn_minus = QPushButton("-")
        btn_minus.setFixedWidth(30)
        btn_minus.setFixedHeight(30)
        btn_minus.setStyleSheet("QPushButton { font-size: 16px; }")
        slider_layout.addWidget(btn_minus)

        # 滑块控件
        slider = QSlider(Qt.Horizontal)
        slider.setRange(0, 100)
        slider.setFixedWidth(200)
        slider.setSingleStep(1)
        slider.blockSignals(True)
        slider.setValue(int((joint_info['default'] - joint_info['min_angle']) / (joint_info['max_angle'] - joint_info['min_angle']) * 100))
        slider.blockSignals(False)
        slider_layout.addWidget(slider,1)

        # 加号按钮
        btn_plus = QPushButton("+")
        btn_plus.setFixedWidth(30)
        btn_plus.setFixedHeight(30)
        btn_plus.setStyleSheet("QPushButton { font-size: 16px; }")
        slider_layout.addWidget(btn_plus)

        # 将滑块布局添加到主布局
        layout.addLayout(slider_layout)

        # 存储关节参数
        slider.joint_name = joint_info["joint_name"]
        slider.joint_index = joint_info['joint_index']
        slider.min_angle = joint_info['min_angle']
        slider.max_angle = joint_info['max_angle']
        slider.value_label = value_label

        # 连接信号
        slider.valueChanged.connect(self.create_slider_handler(slider))
        btn_minus.clicked.connect(lambda: self.adjust_slider_value(slider, -1))
        btn_plus.clicked.connect(lambda: self.adjust_slider_value(slider, 1))

        self.all_sliders.append(slider)
        self.control_layout.addWidget(container)
        self.joint_sliders[joint_info['joint_index']] = slider

    def adjust_slider_value(self, slider, delta):
        """调整滑块值并确保在范围内"""
        new_value = slider.value() + delta
        new_value = max(0, min(100, new_value))
        slider.setValue(new_value)

    def create_slider_handler(self, slider):
        """生成滑块值变化处理函数"""
        def handler(value):
            # 将滑块值转换为实际角度
            angle = np.interp(value, [0, 100], [slider.min_angle, slider.max_angle])
            print(f"关节 {slider.joint_index} 角度: {angle:.8f}")
            slider.value_label.setText(f"{angle:.8f}")
            for joint_data in self.available_joints:
                if joint_data["joint_name"] == slider.joint_name:
                    joint_data["current"] = angle

            self.adjust_joint_position(slider.joint_index, angle)
            # 下发测试
            self.play_to_robot()
        return handler


    def play_to_robot(self):
        if self.is_sync_to_robot is False:
            return
        current_duration = float(self.duration_input.text())
        need_send_values = [round(item["current"], 5) for item in self.available_joints]
        current_action = dict(type="MOVE", positions=need_send_values, duration=current_duration)

        characters = string.ascii_letters + string.digits
        sequence_name = ''.join(random.choices(characters, k=4))
        export_data = dict()
        export_sequence = dict()
        export_sequence[sequence_name] = [current_action]
        export_data["sequences"] = export_sequence
        data = dict(motion=f"{json.dumps(export_data)}")
        asyncio.run_coroutine_threadsafe(self.apix.load_and_play(data) , self.loop)


    def adjust_joint_position(self, joint_index, target_angle):
        """调整指定关节位置"""
        if hasattr(self.physics_thread, 'robotId'):
            try:
                with QMutexLocker(self.physics_thread.mutex):
                    p.setJointMotorControl2(
                        self.physics_thread.robotId,
                        joint_index,
                        p.POSITION_CONTROL,
                        targetPosition=target_angle,
                        force=500
                    )
            except Exception as e:
                print(f"关节控制错误: {str(e)}")

    def on_joint_info(self, joints):
        """接收关节信息并创建滑块"""
        try:
            with QMutexLocker(self.physics_thread.mutex):
                for item in JOINT_DATA_WITH_DEFAULT:
                    for joint in joints:
                        if joint['joint_name'] == item["code"]:
                            joint["default"] = item["default"]
                            joint["current"] = item["current"]
                            self.available_joints.append(joint)
                            break

                # 清除旧控件
                while self.control_layout.count():
                    item = self.control_layout.takeAt(0)
                    widget = item.widget()
                    if widget:
                        widget.deleteLater()

                # 创建新控件
                for joint in self.available_joints:
                    self.create_slider_control(joint)

                self.back_to_default_pose(self.available_joints, 4000)

        except Exception as e:
            print(f"关节信息处理错误: {str(e)}")


    def back_to_default_pose(self, available_joints, delay = 0,  sync = False):
        """返回默认姿态"""
        def init_joint():
            # 显式设置所有关节的初始位置
            for slider in self.all_sliders:
                slider.blockSignals(True)
            for index, joint_info in enumerate(available_joints):
                joint_info['current'] = joint_info['default']
                self.adjust_joint_position( joint_info['joint_index'], joint_info['default'])
                if index < len(self.all_sliders):
                    self.all_sliders[index].setValue(int((joint_info['current'] - joint_info['min_angle']) / (joint_info['max_angle'] - joint_info['min_angle']) * 100))
            # 批量操作后恢复信号
            for slider in self.all_sliders:
                slider.blockSignals(False)

            if sync:
                self.play_to_robot()

        QTimer.singleShot(delay, init_joint)

        # 停止当前动画
        if self.animation_timer.isActive():
            self.animation_timer.stop()

        # 存储初始值
        self.start_yaw = self.cam_yaw
        self.start_pitch = self.cam_pitch
        self.start_distance = self.cam_distance

        # 启动动画
        self.animation_start_time = time.time()
        self.animation_timer.start(20)  # 50fps

    def smooth_reset(self):
        """每帧更新动画"""
        elapsed = time.time() - self.animation_start_time
        progress = min(elapsed / self.reset_duration, 1.0)

        # 插值参数
        self.cam_yaw = self.lerp(self.start_yaw, self.default_cam_yaw, progress)
        self.cam_pitch = self.lerp(self.start_pitch, self.default_cam_pitch, progress)
        self.cam_distance = self.lerp(self.start_distance, self.default_cam_distance, progress)

        # 更新视图
        self.update_view_matrix()
        self.safe_render()

        # 动画结束
        if progress >= 1.0:
            self.animation_timer.stop()

    def lerp(self, a, b, t):
        """线性插值"""
        return a + (b - a) * t


    def updateRender(self):
        try:
            # 使用轻量级渲染器
            img_data = p.getCameraImage(
                width=self.cam_width,
                height=self.cam_height,
                viewMatrix=self.view_matrix,
                projectionMatrix=self.proj_matrix,
                renderer=p.ER_TINY_RENDERER,  # 改用轻量渲染器
                flags=p.ER_NO_SEGMENTATION_MASK
            )

            """ 白色默认背景方案 """
            # # 直接操作字节数据
            # rgb_bytes = bytes(img_data[2][:, :, :3].tobytes())
            #
            # q_img = QImage(
            #     rgb_bytes,
            #     self.cam_width,
            #     self.cam_height,
            #     QImage.Format_RGB888
            # )
            #
            # # 直接设置Pixmap避免中间转换
            # self.render_label.setPixmap(QPixmap.fromImage(q_img))

            """  透明背景方案   """
            # 将numpy数组转换为RGBA格式
            rgb_array = np.array(img_data[2][:, :, :3])
            alpha = np.where(np.all(rgb_array == [255, 255, 255], axis=-1), 0, 255).astype(np.uint8)
            rgba_array = np.dstack((rgb_array, alpha))

            # 创建透明背景的QImage
            q_img = QImage(
                rgba_array.tobytes(),
                self.cam_width,
                self.cam_height,
                QImage.Format_RGBA8888
            )

            # 设置Qt控件透明度
            self.render_label.setAttribute(Qt.WA_TranslucentBackground)
            self.render_label.setPixmap(QPixmap.fromImage(q_img))


            """ 自定义背景颜色方案 """
            # # 转换为numpy数组
            # rgb_array = np.array(img_data[2][:, :, :3])  # shape: (height, width, 3)
            #
            # # 定义新背景颜色（RGB格式，0-255）
            # new_bg_color = [0, 0, 0]  # 深灰色
            #
            # # 创建背景掩码（原始背景是白色）
            # background_mask = (rgb_array[:, :, 0] == 255) & \
            #                   (rgb_array[:, :, 1] == 255) & \
            #                   (rgb_array[:, :, 2] == 255)
            #
            # # 替换背景颜色
            # rgb_array[background_mask] = new_bg_color
            #
            # # 转换为QImage
            # height, width, _ = rgb_array.shape
            # bytes_per_line = 3 * width
            # q_img = QImage(
            #     rgb_array.astype(np.uint8).tobytes(),
            #     width,
            #     height,
            #     bytes_per_line,
            #     QImage.Format_RGB888
            # )
            #
            # self.render_label.setPixmap(QPixmap.fromImage(q_img))

        except Exception as e:
            self.logger.info(f"渲染错误: {str(e)}")

    def closeEvent(self, event):
        self.physics_thread.stop()
        event.accept()

def init_log():
    """ 初始化日志 """
    LogUitl.instance().init(".", "g1_robot")
    return LogUitl.get_logger()


if __name__ == "__main__":
    # logger = init_log()
    # app = QApplication(sys.argv)
    # try:
    #     window = MainWindow(logger)
    #     window.show()
    # except:
    #     error = traceback.format_exc()
    #     logger.info(f"错误: {error}")
    #
    # sys.exit(app.exec_())

    logger = init_log()
    app = QApplication(sys.argv)
    loop = qasync.QEventLoop(app)
    try:
        asyncio.set_event_loop(loop)
        window = MainWindow(logger, loop)
        window.show()
        with loop:
            loop.run_forever()
    except:
        error = traceback.format_exc()
        logger.info(f"错误: {error}")
